﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lumo
{
    /// <summary>
    /// Smoothness
    /// </summary>
    public enum Smooth
    {
        /// <summary> No smoothness </summary>
        None = 0,

        /// <summary> SmoothStart </summary>
        Start = 1,

        /// <summary> SmoothArrival </summary>
        Arrival = 2,

        /// <summary> SmoothStart and Arrival </summary>
        StartArrival = 3
    }    
    
    /// <summary>
    /// Smoothness
    /// </summary>
    public enum Loop
    {
        /// <summary> No smoothness </summary>
        None = 0,

        /// <summary> Zigzag loop mode </summary>
        ZigZag = 1,

        /// <summary> Loop mode from beginning </summary>
        Reset = 2
    }

    /// <summary>
    /// Generic counter.
    /// </summary>
    public class Counter : IUpdatable
    {
        /// <summary>
        /// From value.
        /// </summary>
        public float From { get; private set; }
        /// <summary>
        /// To value.
        /// </summary>
        public float To  { get; private set; }

        // Private variables
        private float Time = 0;
        private Smooth Smoothness = 0;
        private float Current = 0;
        private Loop LoopMode = Loop.None;
        private bool  Growing = false;

        /// <summary>
        /// Constructor.
        /// </summary>
        public Counter() { Reset(); }

        /// <summary>
        /// Constructor.
        /// </summary>
        public Counter(float From, float To, float Time, Smooth Smoothness = Smooth.None, Loop LoopMode = Loop.None)
        {
            Reset(From, To, Time, Smoothness, LoopMode);
        }

        /// <summary>
        /// Reset the counter.
        /// </summary>
        public void Reset()
        {
            this.From = 0;
            this.To = 0;
            this.Time = 0;
            this.Smoothness = Smooth.None;
            this.LoopMode = Loop.None;
            this.Current = 0;
        }

        /// <summary>
        /// Reset the counter.
        /// </summary>
        public void Reset(float From, float To, float Time, Smooth Smoothness = Smooth.None, Loop LoopMode = Loop.None)
        {
            this.From = From;
            this.To = To;
            this.Time = Time;
            this.Smoothness = Smoothness;
            this.LoopMode = LoopMode;
            this.Current = 0;
        }

        /// <summary>
        /// Check if counter has finished.
        /// </summary>
        public bool Finished { get { return (Current >= 1); } }

        /// <summary>
        /// Access the counter value.
        /// </summary>
        public float Value
        {
            get
            {
                if (Smoothness == Smooth.StartArrival)
                    return (float) (From + (To - From) * (1 - Math.Cos(Current * Math.PI)) / 2.0f);
                else if (Smoothness == Smooth.Arrival)
                    return (float) (From + (To - From) * Math.Sin(Current * Math.PI / 2.0f));
                else if (Smoothness == Smooth.Start)
                    return (float) (From + (To - From) * (1 - Math.Cos(Current * Math.PI / 2.0f)));
                else
                    return (float) (From + (To - From) * Current);
            }
            set
            {
                // Check for limits
                bool Start = false;
                bool Finish = false;
                if (From > To)
                {
                    if (value >= From)
                        Start = true;
                    else if (value <= To)
                        Finish = true;
                }
                else
                {
                    if (value <= From)
                        Start = true;
                    else if (value >= To)
                        Finish = true;
                }

                // Inferior limit.
                if (Start)
                {
                    Current = 0;
                    Growing = true;
                }
                // Superior limit.
                else if (Finish)
                {
                    Current = 1;
                    Growing = false;
                }
                // Inside the limits.
                else
                {
                    // Set the value according to the smoothness.
                    float Temp = (value - From) / (To - From);
                    if (Smoothness == Smooth.StartArrival)
                        Current = (float)(Math.Acos(1 - Temp * 2) / Math.PI);
                    else if (Smoothness == Smooth.Arrival)
                        Current = (float)(Math.Asin(Temp) * 2 / Math.PI);
                    else if (Smoothness == Smooth.Start)
                        Current = (float)(Math.Acos(1 - Temp) * 2 / Math.PI);
                    else
                        Current = Temp;
                }
            }
        }

        /// <summary>
        /// Update the counter.
        /// </summary>
        /// <param name="Game">The game class.</param>
        public void Update(LumoComponent Game)
        {
            // Get the time step.
            float TimeStep = (float) Game.GameTime.ElapsedGameTime.TotalMilliseconds;

            // Empty time.
            if (Time <= 0.00001f)
            {
                Current = 1;
                return;
            }

            //
            if (LoopMode == Loop.ZigZag)
            {
                // Complete Steps
                while (TimeStep >= (Time * 2))
                    TimeStep -= (Time * 2);

                // Half Steps
                if (TimeStep >= Time)
                {
                    Growing = !Growing;
                    TimeStep -= Time;
                }

                // Check step.
                float Step = (TimeStep / Time);
                float Temp;

                // While there is to advance
                while (Step > 0.000000001)
                {
                    // Check if growing
                    if (Growing)
                    {
                        Temp = Current + Step;
                        if (Temp > 1)
                        {
                            Current = 1;
                            Step = Temp - 1;
                            Growing = false;
                        }
                        else
                        {
                            Current = Temp;
                            break;
                        }
                    }
                    // Not growing.
                    else
                    {
                        Temp = Current - Step;
                        if (Temp < 0)
                        {
                            Current = 0;
                            Step = -Temp;
                            Growing = true;
                        }
                        else
                        {
                            Current = Temp;
                            break;
                        }
                    }
                }
            }
            //
            else if (LoopMode == Loop.Reset)
            {
                // Complete Steps
                while (TimeStep >= Time)
                    TimeStep -= Time;

                // Check step.
                float Step = (TimeStep / Time);
                float Temp;

                // While there is to advance
                while (Step > 0.000000001)
                {
                    // Check if growing
                    Temp = Current + Step;
                    if (Temp > 1)
                    {
                        Current = 0;
                        Step    = Temp - 1;
                        Growing = true;
                    }
                    else
                    {
                        Current = Temp;
                        break;
                    }
                }
            }
            else
            {
                // Advance the step.
                float Step = (TimeStep / Time);
                Current = Math.Min(Current + Step, 1);
            }
        }
    }
}
